package com.hoe.tsb.tool;

import com.alibaba.fastjson.JSON;
import com.hoe.tsb.fraft.event.msg.IFRaftMessage;
import com.hoe.tsb.fraft.exception.FRaftException;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;

/**
 * <p>
 * 序列化工具
 * </p>
 *
 * @author hoe
 * @version 1.0
 * @date 2022/12/3 14:50
 */
@Slf4j
@UtilityClass
public class SerialUtils {

    /**
     * 基于ObjectOutputStream的序列化方法<br/>
     * 需要保证对象及对象内部的所有成员均实现了Serializable接口<br/>
     * 且实现了Serializable接口的对象最好是手动指定了serialVersionUID字段
     *
     * @param serial 待序列化对象
     * @return 序列化之后的字节数组
     */
    public static byte[] serialize(Serializable serial) {
        try {
            ByteOutputStream bos = new ByteOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(serial);
            oos.flush();
            return bos.getBytes();
        } catch (Exception e) {
            log.error("序列化出错", e);
            throw new FRaftException("序列化出错");
        }
    }

    /**
     * 基于ObjectInputStream的反序列化方法<br/>
     * 具体要求同{@link com.hoe.tsb.tool.SerialUtils#serialize}
     * @param bytes 待反序列化的字节数组
     * @return 反序列化之后的对象，直接强转即可
     */
    public static Object deserialize(byte[] bytes) {
        try {
            ByteInputStream bis = new ByteInputStream(bytes, bytes.length);
            ObjectInputStream ois = new ObjectInputStream(bis);
            return ois.readObject();
        } catch (Exception e) {
            log.error("反序列化出错", e);
            throw new FRaftException("反序列化出错");
        }
    }

    /**
     * 由于这部分对象变化多，且未来可能存在扩展，故不直接使用java原生的Serializable进行序列化<br/>
     * 因为那样对象的灵活性会降低，之所以MMK采用上述方式进行序列化，主要是因为成员变量不能提供getter/setter方法，是由构造函数自动生成的<br/>
     * 这里统一采用FastJSON来进行序列化和反序列化
     * @param msg 待序列化的IFRaft对象
     * @return 序列化之后的字节数组
     */
    public static byte[] serializeIFRaftMsg(IFRaftMessage msg) {
        return JSON.toJSONString(msg).getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 基于FastJSON的反序列化方法<br/>
     * 具体要求同{@link com.hoe.tsb.tool.SerialUtils#serializeIFRaftMsg}
     * @param bytes 待反序列化的字节数组
     * @return 反序列化之后的对象，直接强转即可
     */
    public static <T extends IFRaftMessage> T deserializeIFRaftMsg(byte[] bytes, Class<T> cls) {
        try {
            return JSON.parseObject(new String(bytes, StandardCharsets.UTF_8), cls);
        } catch (Exception e) {
            log.error("基于JSON反序列化出错", e);
            throw new FRaftException("基于JSON反序列化出错");
        }
    }

}
